using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Xfinity.ParticleSystems;

namespace Xfinity.ParticleSystems
{
    /// <summary>
    /// A generic particle emitter.
    /// </summary>
    public abstract class ParticleEmitter
    {

        private float minimumSpeed;

        /// <summary>
        /// Gets or sets the minimum speed.
        /// 
        /// Note, this is not velocity, but the magnitude in any one direction.
        /// </summary>
        /// <value>The minimum speed.</value>
        public float MinimumSpeed
        {
            get { return minimumSpeed; }
            set
            {
                minimumSpeed = value;
                SetSpeedSpan();
            }
        }

        private float maximumSpeed;

        /// <summary>
        /// Gets or sets the maximum speed.
        /// 
        /// Note, this is not velocity, but the magnitude in any one direction.
        /// </summary>
        /// <value>The maximum speed.</value>
        public float MaximumSpeed
        {
            get { return maximumSpeed; }
            set
            {
                maximumSpeed = value;
                SetSpeedSpan();
            }
        }

        /// <summary>
        /// Sets the speed span.
        /// </summary>
        private void SetSpeedSpan()
        {
            speedSpan = maximumSpeed - minimumSpeed;
        }

        private float speedSpan;

        /// <summary>
        /// Gets the speed span.
        /// </summary>
        /// <value>The speed span.</value>
        public float SpeedSpan
        {
            get { return speedSpan; }
        }

        private float minimumAcceleration;

        /// <summary>
        /// Gets or sets the minimum acceleration.
        /// </summary>
        /// <value>The minimum acceleration.</value>
        public float MinimumAcceleration
        {
            get { return minimumAcceleration; }
            set
            {
                minimumAcceleration = value;
                SetAccelerationSpan();
            }
        }
        private float maximumAcceleration;

        /// <summary>
        /// Gets or sets the maximum acceleration.
        /// </summary>
        /// <value>The maximum acceleration.</value>
        public float MaximumAcceleration
        {
            get { return maximumAcceleration; }
            set
            {
                maximumAcceleration = value;
                SetAccelerationSpan();
            }
        }

        /// <summary>
        /// Sets the acceleration span.
        /// </summary>
        private void SetAccelerationSpan()
        {
            accelerationSpan = maximumAcceleration - minimumAcceleration;
        }
        private float accelerationSpan;

        /// <summary>
        /// Gets the acceleration span.
        /// </summary>
        /// <value>The acceleration span.</value>
        public float AccelerationSpan
        {
            get { return accelerationSpan; }
        }


        private float deathSpan;

        /// <summary>
        /// Gets the death span.
        /// </summary>
        /// <value>The death span.</value>
        public float DeathSpan
        {
            get { return deathSpan; }
        }

        private float minimumDeathAge;

        /// <summary>
        /// Gets or sets the minimum death age.
        /// </summary>
        /// <value>The minimum death age.</value>
        public float MinimumDeathAge
        {
            get { return minimumDeathAge; }
            set
            {
                minimumDeathAge = value;
                SetDeathSpan();
            }
        }

        /// <summary>
        /// Sets the death span.
        /// </summary>
        private void SetDeathSpan()
        {
            deathSpan = maximumDeathAge - minimumDeathAge;
        }
        private float maximumDeathAge;

        /// <summary>
        /// Gets or sets the maximum death age.
        /// </summary>
        /// <value>The maximum death age.</value>
        public float MaximumDeathAge
        {
            get { return maximumDeathAge; }
            set
            {
                maximumDeathAge = value;
                SetDeathSpan();
            }
        }


        private Color minimumColorValue;

        /// <summary>
        /// Sets the color span.
        /// </summary>
        private void SetColorSpan()
        {
            colorSpan = new Color(maximumColorValue.ToVector4() - minimumColorValue.ToVector4());
        }
        /// <summary>
        /// Gets or sets the minimum color value.
        /// </summary>
        /// <value>The minimum color value.</value>
        public Color MinimumColorValue
        {
            get { return minimumColorValue; }
            set
            {
                minimumColorValue = value;
                SetColorSpan();
            }
        }

        private Color maximumColorValue;

        /// <summary>
        /// Gets or sets the maximum color value.
        /// </summary>
        /// <value>The maximum color value.</value>
        public Color MaximumColorValue
        {
            get { return maximumColorValue; }
            set
            {
                maximumColorValue = value;
                SetColorSpan();
            }
        }

        private Color colorSpan;

        /// <summary>
        /// Gets the color span.
        /// </summary>
        /// <value>The color span.</value>
        public Color ColorSpan
        {
            get { return colorSpan; }
        }

        private float minimumSize;

        /// <summary>
        /// Gets or sets the minimum size.
        /// </summary>
        /// <value>The minimum size.</value>
        public float MinimumSize
        {
            get { return minimumSize; }
            set
            {
                minimumSize = value;
                SetSizeSpan();
            }
        }

        /// <summary>
        /// Sets the size span.
        /// </summary>
        private void SetSizeSpan()
        {
            sizeSpan = maximumSize - minimumSize;
        }
        private float maximumSize;

        /// <summary>
        /// Gets or sets the maximum size.
        /// </summary>
        /// <value>The maximum size.</value>
        public float MaximumSize
        {
            get { return maximumSize; }
            set
            {
                maximumSize = value;
                SetSizeSpan();
            }
        }
        private float sizeSpan;

        /// <summary>
        /// Gets the size span.
        /// </summary>
        /// <value>The size span.</value>
        public float SizeSpan
        {
            get { return sizeSpan; }
        }



        /// <summary>
        /// Generates a new particle.
        /// </summary>
        /// <returns> A new particle.</returns>
        public Particle GenerateNewParticle()
        {
            Vector3 initialPosition = GenerateInitialPosition();
            Vector3 initialVelocity = GenerateInitialVelocity();
            Vector3 initialAcceleration = GenerateInitialAcceleration();

            // float mass = GenerateMass();
            float size = GenerateInitialSize();
            float deathAge = GenerateDeathAge();
            Color color = GenerateInitialColor();

            return new Particle(initialPosition, initialVelocity, initialAcceleration, /*mass,*/ size, deathAge, color);
        }

        /// <summary>
        /// Generates the life span of the particle.
        /// </summary>
        /// <returns>When the particle will die.</returns>
        protected abstract float GenerateDeathAge();

        private Vector3 centrePosition;

        /// <summary>
        /// Gets or sets the centre position of the emitter.
        /// </summary>
        /// <value>The centre position.</value>
        public Vector3 CentrePosition
        {
            get { return centrePosition; }
            set { centrePosition = value; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParticleEmitter"/> class.
        /// </summary>
        /// <param name="centrePosition">The centre position.</param>
        protected ParticleEmitter(Vector3 centrePosition)
        {
            this.centrePosition = centrePosition;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParticleEmitter"/> class.
        /// </summary>
        protected ParticleEmitter()
            : this(Vector3.Zero)
        { }

        /// <summary>
        /// Generates an initial colour for the particle.
        /// </summary>
        /// <returns>The starting colour.</returns>
        protected abstract Color GenerateInitialColor();
        /// <summary>
        /// Generates the initial size of the particle.
        /// </summary>
        /// <returns>The size.</returns>
        protected abstract float GenerateInitialSize();
        // protected abstract float GenerateMass();
        /// <summary>
        /// Generates the initial acceleration of the particle.
        /// </summary>
        /// <returns>The initial acceleration.</returns>
        protected abstract Vector3 GenerateInitialAcceleration();
        /// <summary>
        /// Generates the initial velocity of the particle.
        /// </summary>
        /// <returns>The initial velocity.</returns>
        protected abstract Vector3 GenerateInitialVelocity();
        /// <summary>
        /// Generates the initial position for the particle.
        /// </summary>
        /// <returns>The initial position.</returns>
        protected abstract Vector3 GenerateInitialPosition();
    }
}